package com.uh.rds.tester.data;

import com.uh.console.domain.RdsNode;
import com.uh.console.domain.vo.RdsServiceNodesVo;
import com.uh.rds.testing.config.TestConfig;
import com.uh.rds.testing.config.TestConfigManager;
import com.uh.rds.testing.conn.RdsConnectInfo;
import com.uh.console.client.ConsoleWebClient;
import com.uh.console.client.RdsNodeActions;
import com.uh.console.client.RdsServiceHelper;
import com.uh.rds.testing.data.FileTask;
import com.uh.rds.testing.data.TestDataGenerator;
import com.uh.rds.testing.utils.RdsConnectionUtils;
import com.uh.rds.testing.validator.DataValidatorArguments;
import com.uh.rds.testing.validator.DataValidatorResult;
import com.uh.rds.testing.validator.DataValidatorRunner;
import com.uh.rds.testing.validator.types.*;
import org.junit.jupiter.api.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.List;

import static com.uh.rds.testing.config.TestConfigManager.getConsoleClientConf;
import static com.uh.rds.testing.base.DataValidatorStep.*;

/**
 *
 *   点的备份存盘（cfg.xml中Server.DataDump设置为0),
 * 单节点模式下测试
 *  1. 测试数据高速并发写入后能否正确读出
 *  2. 测试RDS重启后数据是否一致
 *  3. 测试数据是否能正确删除
 *
 */
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SingleNodeTest {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    private ConsoleWebClient client;
    private RdsServiceNodesVo serviceNodesVo;
    private RdsConnectInfo connection;
    private TestConfig conf;
    private DataValidatorRunner tester;

    File dataBase = new File("data/SingleNodeTest");
    File stringDataFile = new File(dataBase, "string-data.csv");
    File setDataFile = new File(dataBase, "set-data.csv");
    File hashDataFile = new File(dataBase, "hash-data.csv");
    File listDataFile = new File(dataBase, "list-data.csv");
    File zsetDataFile = new File(dataBase, "zset-data.csv");
    File streamDataFile = new File(dataBase, "stream-data.csv");

    /**
     * 初始化测试变量
     *  - 读取测试配置到 conf
     *  - 获取被测试的 RDS服务 信息
     */
    @BeforeAll
    public void init() {
        logger.info("SingleNodeTest init ...");
        conf = TestConfigManager.getTestConfig("SingleNodeTest");
        client = ConsoleWebClient.getInstance(getConsoleClientConf());
        client.login();

        Long testServiceId = conf.getLong("testServiceId");
        serviceNodesVo = RdsServiceHelper.getServiceInfo(testServiceId, client);
        connection = RdsConnectionUtils.getRdsConnection(serviceNodesVo);


        DataValidatorArguments config = new DataValidatorArguments();
        config.setThreadCount(conf.getInteger("threadCount"));
        config.setValidateInterval(conf.getLong("validateInterval"));
        config.setValidateFromSlave(false);
        config.setRandomOrder(true);
        tester = new DataValidatorRunner(config);
        tester.addValidator(StringDataValidator.class, stringDataFile);
        tester.addValidator(SetDataValidator.class, setDataFile);
        tester.addValidator(HashDataValidator.class, hashDataFile);
        tester.addValidator(ListDataValidator.class, listDataFile);
        tester.addValidator(ZsetDataValidator.class, zsetDataFile);
        tester.addValidator(StreamDataValidator.class, streamDataFile);
    }

    /**
     * 生成测试数据，第一次生成后之后可重复使用。
     * 会判断数据文件是否存在，如果存在则不再生成。
     */
    @Test
    @Order(1)
    public void generateTestData() {

        int dataCount = conf.getInteger("dataCount");
        int subDataCount = conf.getInteger("subDataCount");
        TestDataGenerator dataGenerator = new TestDataGenerator();
        dataGenerator.addNoExistTask(new FileTask(stringDataFile, "string", dataCount, 1));
        dataGenerator.addNoExistTask(new FileTask(setDataFile, "set", dataCount, subDataCount));
        dataGenerator.addNoExistTask(new FileTask(hashDataFile, "hash", dataCount, subDataCount));
        dataGenerator.addNoExistTask(new FileTask(listDataFile, "list", dataCount, subDataCount));
        dataGenerator.addNoExistTask(new FileTask(zsetDataFile, "zset", dataCount, subDataCount));
        dataGenerator.addNoExistTask(new FileTask(streamDataFile, "stream", dataCount, subDataCount));


        int taskCount = dataGenerator.getTaskCount();
        if(taskCount > 0) {
            logger.info("Generating test data ...");
            int executed = dataGenerator.executeTasks();
            Assertions.assertEquals(taskCount, executed);
        }
        else {
            logger.info("Test data files already exists, skip generating ...");
        }


    }

    /**
     * 测试各类型数据高速写入后，是否能正确读出。
     *
     */
    @Test
    @Order(2)
    public void testData() throws Exception {
        if(! tester.isReady()) {
            tester.prepareThreadsData(connection);
        }
        RdsConnectionUtils.flushDb(connection);
        DataValidatorResult result = tester.runTest(PROCESS, VALIDATE);
        System.out.println(result.summary());
        Assertions.assertTrue(result.isPassed());
    }


    /**
     * 测试重启后数据是否一致。
     */
    @Test
    @Order(3)
    public void testDataReload() throws Exception {
        if(! tester.isReady()) {
            tester.prepareThreadsData(connection);
        }
        //清空数据
        RdsConnectionUtils.flushDb(connection);
        //读取测试数据并灌入(同时校验是否一致）
        DataValidatorResult result1 = tester.runTest(PROCESS, VALIDATE);
        Assertions.assertTrue(result1.isPassed()); //断言执行成功


        //重启节点
        RdsNodeActions nodeAct = client.getRdsNodeActions();
        List<RdsNode> rdsNodes = serviceNodesVo.getNodes();
        for (RdsNode node : rdsNodes) {
            nodeAct.stopNodeWait(node.getNodeId());  //停止
            logger.info("Node(ID={}, name={}) stopped, waiting for restart...", node.getNodeId(), node.getNodeName());
            Thread.sleep(10000);
            nodeAct.startNodeWait(node.getNodeId()); //启动
        }


        //重新获取连接，因服务节点重启之前的连接已经失效
        connection = RdsConnectionUtils.getRdsConnection(serviceNodesVo);
        DataValidatorResult result2 = tester.runTest(VALIDATE, CLEAR);
        System.out.println(result2.summary());
        Assertions.assertTrue(result2.isPassed());
    }


    @Test
    @Order(4)
    public void deleteDataFiles() {
        logger.info("Deleting test data files...");
        stringDataFile.deleteOnExit();
        setDataFile.deleteOnExit();
        hashDataFile.deleteOnExit();
        listDataFile.deleteOnExit();
        zsetDataFile.deleteOnExit();
        streamDataFile.deleteOnExit();
    }

}
